package comp9417;

import java.io.Console;
import java.io.FileNotFoundException;

/**
 * Created by huy on 3/06/14.
 */
public class Regression {

    private Console console;
    private int minK = 2;
    private int maxK = 10;
    private int folds = 10;
    private int k = 2;
    private boolean weighted = false;

    public Regression(Console console) {
        this.console = console;
    }

    public static void main(String[] args) throws FileNotFoundException {
        Console console = System.console();
        if (console == null) {
            System.out.println("Cannot start console.");
            System.exit(0);
        }
        Regression regression = new Regression(console);
        regression.go();
    }

    private void go() throws FileNotFoundException {
        int option = 1;
        Dataset dataset = null;
        String regressionAttributeName = null;
        Attribute regressionAttribute = null;
        DataPoint datapoint = null;
        while (option > 0) {
            if (option <= 1) {
                console.printf("Enter data arff data file:");
                String fileName = console.readLine();
                dataset = new DataLoader().loadClassificationData(fileName);
            }
            if (option <= 2) {
                console.printf("Loaded dataset with %d datapoints. The followings are the attributes found: \n", dataset.getDatapoints().size());
                for (Attribute attribute : dataset.getAttributes()) {
                    console.printf("%s %s\n", attribute.name, (attribute.prune ? "(pruned)" : ""));
                }
                console.printf("Enter attribute for regression:");
                regressionAttributeName = console.readLine();
                k = parseInteger("Enter k [%d or 0 to auto-select k]:", k);
                weighted = parseBoolean("Use weighted distance [%b]:", weighted);
                if (k == 0) {
                    k = new Evaluator(dataset, regressionAttributeName).bestK(minK, maxK, folds, weighted);
                    System.out.println("k:" + k);
                }
                regressionAttribute = dataset.getAttribute(regressionAttributeName);
                if (regressionAttribute.prune) {
                    System.out.println(regressionAttribute.type.prunedValue(regressionAttribute));
                } else {
                    datapoint = new DataPoint(dataset.getAttributes().length);
                    for (int index = 0; index < dataset.getAttributes().length; index++) {
                        Attribute attribute = dataset.getAttributes()[index];
                        if (attribute.prune || attribute.name.equals(regressionAttributeName)) continue;
                        if (AttributeType.CATEGORICAL.equals(attribute.type)) {
                            String entry = "";
                            while (!attribute.categories.keySet().contains(entry)) {
                                console.printf("Enter value for %s %s:", attribute.name, attribute.categories.keySet().toString().replace(",]", "]"));
                                entry = console.readLine();
                            }
                            datapoint.addValue(index, entry);
                        } else {
                            console.printf("Enter value for %s [%f - %f]", attribute.name, attribute.min, attribute.max);
                            datapoint.addValue(index, Double.parseDouble(console.readLine()));
                        }
                    }
                }
            }
            if (!regressionAttribute.prune && option <= 3) {
                k = parseInteger("Enter k [%d or 0 to auto-select k]:", k);
                weighted = parseBoolean("Use weighted distance [%b]:", weighted);
                if (k == 0) {
                    k = new Evaluator(dataset, regressionAttributeName).bestK(minK, maxK, folds, weighted);
                    System.out.println("k:" + k);
                }
                Classifier classifier = new Classifier(dataset, regressionAttributeName);
                AttributeType type = regressionAttribute.type;
                Object prediction = weighted ? type.predictWeighted(classifier, datapoint, k) : type.predict(classifier, datapoint, k);
                console.printf("%s : %s\n", regressionAttributeName, prediction);
            }

            option = parseInteger("Do you want to 0) exit 1) load new dataset, 2) change attribute or 3) change settings:\n", 0);
        }
    }

    private boolean parseBoolean(String prompt, boolean defaultValue) {
        console.printf(prompt, defaultValue);
        return Boolean.parseBoolean(console.readLine().trim());
    }

    private int parseInteger(String prompt, int defaultValue) {
        int parsed;
        try {
            console.printf(prompt, defaultValue);
            parsed = Integer.parseInt(console.readLine().trim());
        } catch (NumberFormatException ex) {
            parsed = defaultValue;
        }
        return parsed;
    }
}
